<!DOCTYPE html>
<html>

<head>
    <title>model animation</title>
    <script type="text/javascript" src="https://unpkg.com/dat.gui@0.7.6/build/dat.gui.min.js"></script>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
    <script type="text/javascript" src="https://unpkg.com/maptalks/dist/maptalks.js"></script>
    <script type="text/javascript" src="https://unpkg.com/three@0.138.0/build/three.min.js"></script>
    <script type="text/javascript"
        src="https://unpkg.com/three@0.138.0/examples/js/loaders/GLTFLoader.js"></script>
    <script type="text/javascript" src="https://unpkg.com/maptalks.three@latest/dist/maptalks.three.js"></script>
    <script type="text/javascript"
        src="https://unpkg.com/three@0.138.0/examples/js/libs/stats.min.js"></script>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
            width: 100%;
        }

        #map {
            width: 100%;
            height: 100%;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>

        var map = new maptalks.Map("map", {
            center: [19.06325670775459, 42.16842479475318],
            zoom: 9,
            pitch: 60,
            // bearing: 180,

            centerCross: true,
            doubleClickZoom: false,
            baseLayer: new maptalks.TileLayer('tile', {
                urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
                subdomains: ['a', 'b', 'c', 'd'],
                attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
            })
        });

        // the ThreeLayer to draw buildings
        var threeLayer = new maptalks.ThreeLayer('t', {
            forceRenderOnMoving: true,
            forceRenderOnRotating: true
            // animation: true
        });
        threeLayer.prepareToDraw = function (gl, scene, camera) {
            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(0, -10, 10).normalize();
            scene.add(light);
            camera.add(new THREE.PointLight('#fff', 4));

            addGltf();

        };
        threeLayer.addTo(map);


        var stats, clock, gui, mixer, actions, activeAction, previousAction;
        var model, face;
        var api = { state: 'Walking' };


        function addGltf() {
            clock = new THREE.Clock();
            stats = new Stats();
            map.getContainer().appendChild(stats.dom);
            var loader = new THREE.GLTFLoader();
            loader.load('./data/RobotExpressive.glb', function (gltf) {

                model = gltf.scene;
                model.rotation.x = Math.PI / 2;
                model.scale.set(100, 100, 100);
                model.position.copy(threeLayer.coordinateToVector3(map.getCenter()));
                threeLayer.addMesh(model);

                createGUI(model, gltf.animations);
                animate();

            }, undefined, function (e) {

                console.error(e);

            });
        }


        function createGUI(model, animations) {

            var states = ['Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing'];
            var emotes = ['Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp'];

            gui = new dat.GUI();

            mixer = new THREE.AnimationMixer(model);

            actions = {};

            for (var i = 0; i < animations.length; i++) {

                var clip = animations[i];
                var action = mixer.clipAction(clip);
                actions[clip.name] = action;

                if (emotes.indexOf(clip.name) >= 0 || states.indexOf(clip.name) >= 4) {

                    action.clampWhenFinished = true;
                    action.loop = THREE.LoopOnce;

                }

            }

            // states

            var statesFolder = gui.addFolder('States');

            var clipCtrl = statesFolder.add(api, 'state').options(states);

            clipCtrl.onChange(function () {

                fadeToAction(api.state, 0.5);

            });

            statesFolder.open();

            // emotes

            var emoteFolder = gui.addFolder('Emotes');

            function createEmoteCallback(name) {

                api[name] = function () {

                    fadeToAction(name, 0.2);

                    mixer.addEventListener('finished', restoreState);

                };

                emoteFolder.add(api, name);

            }

            function restoreState() {

                mixer.removeEventListener('finished', restoreState);

                fadeToAction(api.state, 0.2);

            }

            for (var i = 0; i < emotes.length; i++) {

                createEmoteCallback(emotes[i]);

            }

            emoteFolder.open();

            // expressions

            face = model.getObjectByName('Head_2');

            var expressions = Object.keys(face.morphTargetDictionary);
            var expressionFolder = gui.addFolder('Expressions');

            for (var i = 0; i < expressions.length; i++) {

                expressionFolder.add(face.morphTargetInfluences, i, 0, 1, 0.01).name(expressions[i]);

            }

            activeAction = actions['Walking'];
            activeAction.play();

            expressionFolder.open();

        }

        function fadeToAction(name, duration) {
            previousAction = activeAction;
            activeAction = actions[name];
            if (previousAction !== activeAction) {
                previousAction.fadeOut(duration);
            }
            activeAction
                .reset()
                .setEffectiveTimeScale(1)
                .setEffectiveWeight(1)
                .fadeIn(duration)
                .play();

        }

        function animate() {
            var dt = clock.getDelta();
            if (mixer) mixer.update(dt);
            requestAnimationFrame(animate);
            stats.update();
            // threeLayer._needsUpdate = !threeLayer._needsUpdate;
            if (threeLayer._needsUpdate) {
                threeLayer.redraw();
            }

        }

    </script>
</body>

</html>
